package com.zc.im.connector.handler;

import com.zc.im.common.constant.enums.HeartBeatEnum;
import com.zc.im.common.core.domain.BaseUserInfo;
import com.zc.im.common.msg.HeartBeatMsg;
import com.zc.im.connector.listener.ChannelContext;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static io.netty.handler.timeout.IdleState.READER_IDLE;

/**
 * Project：im
 * Date：2022/4/21
 * Time：15:55
 * Description：心跳检测handler
 *
 * @author Challen.Zhang
 * @version 1.0
 */
@Slf4j
@Service
@ChannelHandler.Sharable
public class HeartBeatServerHandler extends AbstractChannelInboundHandler<HeartBeatMsg> {

    @Autowired
    private ChannelContext channelContext;

    private final Map<String, Object> lockMap = new ConcurrentHashMap<>();

    private final Map<String, Integer> readIdleTimeMap = new HashMap<>();

    @Override
    //@SuppressWarnings("uncheck")
    protected void channelReadHandle(ChannelHandlerContext ctx, HeartBeatMsg heartBeatMsg) {
        if (HeartBeatEnum.PING == heartBeatMsg.getHeartBeatEnum()) {
            Object o = getLock(heartBeatMsg.getUniqueKey());
            synchronized (o) {
                readIdleTimeMap.put(heartBeatMsg.getUniqueKey(), 0);
            }
            HeartBeatMsg heartBeat = new HeartBeatMsg();
            heartBeat.setToken(heartBeatMsg.getToken());
            heartBeat.setHeartBeatEnum(HeartBeatEnum.PONG);
            ctx.channel().writeAndFlush(heartBeat);
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        IdleStateEvent event = (IdleStateEvent) evt;
        //读空闲
        if (READER_IDLE == event.state()) {
            BaseUserInfo userInfo = channelContext.getUserInfo(ctx.channel().id());
            if (userInfo == null) {
                ctx.channel().close();
                return;
            }
            Object o = getLock(userInfo.getUniqueKey());
            synchronized (o) {
                Integer integer = readIdleTimeMap.get(userInfo.getUniqueKey());
                if (integer >= 3) {
                    // 断开连接 下线
                    channelContext.offline(userInfo, true);
                    ctx.channel().close();
                }
            }
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.err.println("=== " + ctx.channel().remoteAddress() + " is active ===");
    }

    private Object getLock(String uniqueKey) {
        Object o = lockMap.get(uniqueKey);
        if (o == null) {
            Object temp = new Object();
            if (lockMap.putIfAbsent(uniqueKey, temp) != null) {
                return lockMap.get(uniqueKey);
            }
            o = temp;
        }
        return o;
    }
}
